﻿namespace com.crownpack.SharePoint
{
    using System;
    using System.Collections.Generic;
    using System.Net.Mail;
    using System.Threading;

    using Microsoft.SharePoint;

    public class SPSendEmail
    {
        #region Fields

        const string HTMLBEGTAG = "<html>";
        const string HTMLENDTAG = "</html>";

        static SPSendEmail _Instance;

        #endregion Fields

        #region Constructors

        /// <summary>
        /// Creates an instance that can send an email using the System.Net.Mail.SmtpClient utilizing the SharePoint associated web application SMTP configuration.
        /// </summary>
        public SPSendEmail( )
        {
        }

        #endregion Constructors

        #region Events

        /// <summary>
        /// Occurs when an asynchronous e-mail send operation completes. The operation identifier is returned in the System.ComponentModel.AsyncCompletedEventArgs UserState of type System.Guid.
        /// </summary>
        public event SendCompletedEventHandler SendCompleted;

        #endregion Events

        #region Properties

        /// <summary>
        /// Creates and returns one and only one instance of SPSendEmail.
        /// Example: SPSendEmail.Instance.SendEmail( url, subject, body, to, cc, bcc );
        /// </summary>
        public static SPSendEmail Instance
        {
            get
            {
                lock ( _Instance )
                {
                    if ( _Instance == null )
                        _Instance = new SPSendEmail( );
                    return _Instance;
                }
            }
        }

        public EmailSpecifics InternalEmailSpecifics
        {
            get;
            private set;
        }

        #endregion Properties

        #region Methods

        /// <summary>
        /// Sends the e-mail message to the SMTP server defined for the web application for delivery. The web application is associated with the SPSite as specified by the siteUrl. A SPSite object is created based upon the siteUrl parameter. From the SPSite object the web application is obtained.
        /// Example: 
        /// List&lt;MailAddress&gt; to = new List&lt;MailAddress&gt;( );
        /// to.Add( new MailAddress( "emailaddress@somewhere.com", "First Last Name" ) );
        /// List&lt;MailAddress&gt; cc = new List&lt;MailAddress&gt;( );
        /// SPSendEmail spSendEmail = new SPSendEmail( );
        /// bool ok = spSendEmail.SendEmail( url, subject, body, to );
        /// </summary>
        /// <param name="siteUrl">The site collection URL.</param>
        /// <param name="subject">The subject line for this message.</param>
        /// <param name="body">The message body.</param>
        /// <param name="to">A Generic List of type System.Net.Mail.MailAddress that contains the addresses of the recipients.</param>
        /// <returns>true if successful; other false</returns>
        public bool SendEmail( string siteUrl, string subject, string body, List<MailAddress> to )
        {
            return SendEmail( siteUrl, subject, body, to, null, null );
        }

        /// <summary>
        /// Sends the e-mail message to the SMTP server defined for the web application for delivery. The web application is associated with the SPSite as specified by the siteUrl. A SPSite object is created based upon the siteUrl parameter. From the SPSite object the web application is obtained.
        /// Example: 
        /// MailAddress to = new MailAddress( "emailaddress@somewhere.com", "First Last Name" );
        /// SPSendEmail spSendEmail = new SPSendEmail( );
        /// bool ok = spSendEmail.SendEmail( url, subject, body, to );
        /// </summary>
        /// <param name="siteUrl">The site collection URL.</param>
        /// <param name="subject">The subject line for this message.</param>
        /// <param name="body">The message body.</param>
        /// <param name="to">An instance of System.Net.Mail.MailAddress that contains the address of the recipient.</param>
        /// <returns>true if successful; other false</returns>
        public bool SendEmail( string siteUrl, string subject, string body, MailAddress to )
        {
            List<MailAddress> t = new List<MailAddress>( );
            t.Add( to );
            return SendEmail( siteUrl, subject, body, t, null, null );
        }

        /// <summary>
        /// Sends the e-mail message to the SMTP server defined for the web application for delivery. The web application is associated with the SPSite as specified by the siteUrl. A SPSite object is created based upon the siteUrl parameter. From the SPSite object the web application is obtained.
        /// Example: 
        /// List&lt;MailAddress&gt; to = new List&lt;MailAddress&gt;( );
        /// to.Add( new MailAddress( "emailaddress@somewhere.com", "First Last Name" ) );
        /// List&lt;MailAddress&gt; cc = new List&lt;MailAddress&gt;( );
        /// cc.Add( new MailAddress( "emailaddress@somewhere.com", "First Last Name" ) );
        /// List&lt;MailAddress&gt; bcc = new List&lt;MailAddress&gt;( );
        /// bcc.Add( new MailAddress( "emailaddress@somewhere.com", "First Last Name" ) );
        /// SPSendEmail spSendEmail = new SPSendEmail( );
        /// bool ok = spSendEmail.SendEmail( url, subject, body, to, cc, bcc );
        /// </summary>
        /// <param name="siteUrl">The site collection URL.</param>
        /// <param name="subject">The subject line for this message.</param>
        /// <param name="body">The message body.</param>
        /// <param name="to">A Generic List of type System.Net.Mail.MailAddress that contains the addresses of the recipients.</param>
        /// <param name="cc">A Generic List of type System.Net.Mail.MailAddress that contains the addresses of the CC (carbon copy) recipients.</param>
        /// <param name="bcc">A Generic List of type System.Net.Mail.MailAddress that contains the addresses of the BCC (blind carbon copy) recipients.</param>
        /// <returns>true if successful; other false</returns>
        public bool SendEmail( string siteUrl, string subject, string body, List<MailAddress> to, List<MailAddress> cc, List<MailAddress> bcc )
        {
            bool ok = false;
            for ( int i = 0; i < 3; i++ )
            {
                try
                {
                    setEmailSpecifics( siteUrl );
                    if ( InternalEmailSpecifics.IsConfigured )
                    {
                        MailMessage mailMessage = formatMailMessage( subject, body, to, cc, bcc );

                        SmtpClient smtpClient = new SmtpClient( );
                        smtpClient.Host = InternalEmailSpecifics.OutboundMailServerAddress;
                        smtpClient.Send( mailMessage );

                        ok = true;
                    }
                }
                catch
                {
                    Thread.Sleep( 500 );
                }
                if ( ok )
                    break;
            }
            return ok;
        }

        /// <summary>
        /// Sends the e-mail message to the SMTP server defined for the web application for delivery. The web application is associated with the SPSite as specified by the siteUrl. A SPSite object is created based upon the siteUrl parameter. From the SPSite object the web application is obtained. This method does not block the calling thread. An operation identifier is returned from this method of type System.Guid. When the operation completes an event is raised if an event handler has been defined. The operation identifier is returned to the event handler in the AsyncCompletedEventArgs.UserState.
        ///         /// Example: 
        /// List&lt;MailAddress&gt; to = new List&lt;MailAddress&gt;( );
        /// to.Add( new MailAddress( "emailaddress@somewhere.com", "First Last Name" ) );
        /// List&lt;MailAddress&gt; cc = new List&lt;MailAddress&gt;( );
        /// cc.Add( new MailAddress( "emailaddress@somewhere.com", "First Last Name" ) );
        /// List&lt;MailAddress&gt; bcc = new List&lt;MailAddress&gt;( );
        /// bcc.Add( new MailAddress( "emailaddress@somewhere.com", "First Last Name" ) );
        /// SPSendEmail spSendEmail = new SPSendEmail( );
        /// spSendEmail.SendCompleted += new SendCompletedEventHandler( spSendEmail_SendCompleted );
        /// Guid operationId = spSendEmail.SendEmailAsync( url, subject, body, to, cc, bcc );
        /// /// &lt;summary&gt;
        /// /// SendCompletedEventHandler - The method that will handle the System.Net.Mail.SmtpClient.SendCompleted event.
        /// /// &lt;/summary&gt;
        /// /// &lt;param name="sender"&gt;The source of the event.&lt;/param&gt;
        /// /// &lt;param name="e"&gt;An System.ComponentModel.AsyncCompletedEventArgs containing event data. The operation identifier is returned in the UserState of type System.Guid.&lt;/param&gt;
        /// void spSendEmail_SendCompleted( object sender, System.ComponentModel.AsyncCompletedEventArgs e )
        /// {
        ///     Guid operationId = (Guid) e.UserState;
        /// }
        /// </summary>
        /// <param name="siteUrl">The site collection URL.</param>
        /// <param name="subject">The subject line for this message.</param>
        /// <param name="body">The message body.</param>
        /// <param name="to">A Generic List of type System.Net.Mail.MailAddress that contains the addresses of the recipients.</param>
        /// <param name="cc">A Generic List of type System.Net.Mail.MailAddress that contains the addresses of the CC (carbon copy) recipients.</param>
        /// <param name="bcc">A Generic List of type System.Net.Mail.MailAddress that contains the addresses of the BCC (blind carbon copy) recipients.</param>
        /// <returns>an operation identifier of type System.Guid</returns>
        public Guid SendEmailAsync( string siteUrl, string subject, string body, List<MailAddress> to, List<MailAddress> cc, List<MailAddress> bcc )
        {
            Guid guid = Guid.Empty;
            try
            {
                setEmailSpecifics( siteUrl );
                if ( InternalEmailSpecifics.IsConfigured )
                {
                    MailMessage mailMessage = formatMailMessage( subject, body, to, cc, bcc );

                    SmtpClient smtpClient = new SmtpClient( );
                    smtpClient.Host = InternalEmailSpecifics.OutboundMailServerAddress;

                    guid = Guid.NewGuid( );
                    smtpClient.SendCompleted -= new SendCompletedEventHandler( smtpClient_SendCompleted );
                    smtpClient.SendCompleted += new SendCompletedEventHandler( smtpClient_SendCompleted );
                    smtpClient.SendAsync( mailMessage, guid );
                }
            }
            catch
            {
            }
            return guid;
        }

        MailMessage formatMailMessage( string subject, string body, List<MailAddress> to, List<MailAddress> cc, List<MailAddress> bcc )
        {
            if ( InternalEmailSpecifics.IsConfigured )
            {
                MailMessage mailMessage = new MailMessage( );

                mailMessage.From = new MailAddress( InternalEmailSpecifics.OutboundMailSenderAddress );

                mailMessage.ReplyTo = new MailAddress( InternalEmailSpecifics.OutboundMailReplyToAddress );

                if ( to != null && to.Count > 0 )
                    to.ForEach( a => mailMessage.To.Add( a ) );

                if ( cc != null && cc.Count > 0 )
                    cc.ForEach( a => mailMessage.CC.Add( a ) );

                if ( bcc != null && bcc.Count > 0 )
                    bcc.ForEach( a => mailMessage.Bcc.Add( a ) );

                mailMessage.Subject = subject;

                mailMessage.IsBodyHtml = true;

                body = body.Trim( );
                if ( body.StartsWith( HTMLBEGTAG, StringComparison.OrdinalIgnoreCase ) )
                    body = body.Remove( 0, HTMLBEGTAG.Length );
                if ( body.EndsWith( HTMLENDTAG, StringComparison.OrdinalIgnoreCase ) )
                    body = body.Remove( body.Length - HTMLENDTAG.Length );

                mailMessage.Body = string.Format( "{0}{1}{2}", HTMLBEGTAG, body, HTMLENDTAG );
                return mailMessage;
            }
            return null;
        }

        void setEmailSpecifics( string siteUrl )
        {
            using ( SPSite spSite = new SPSite( siteUrl ) )
            {
                InternalEmailSpecifics = new EmailSpecifics
                {
                    OutboundMailReplyToAddress = spSite.WebApplication.OutboundMailReplyToAddress,
                    OutboundMailSenderAddress = spSite.WebApplication.OutboundMailSenderAddress,
                    OutboundMailServerAddress = spSite.WebApplication.OutboundMailServiceInstance.Server.Address
                };
            }
        }

        void smtpClient_SendCompleted( object sender, System.ComponentModel.AsyncCompletedEventArgs e )
        {
            if ( SendCompleted != null )
                SendCompleted( sender, e );
        }

        #endregion Methods

        #region Nested Types

        public class EmailSpecifics
        {
            #region Fields

            const string DEFAULTFROM = "noreply@noreply.com";
            const string DEFAULTREPLYTO = "noreply@noreply.com";

            string _OutboundMailReplyToAddress;
            string _OutboundMailSenderAddress;

            #endregion Fields

            #region Properties

            public bool IsConfigured
            {
                get
                {
                    return !string.IsNullOrEmpty( OutboundMailServerAddress );
                }
            }

            public string OutboundMailReplyToAddress
            {
                get
                {
                    if ( string.IsNullOrEmpty( _OutboundMailReplyToAddress ) )
                        _OutboundMailReplyToAddress = DEFAULTREPLYTO;
                    return _OutboundMailReplyToAddress;
                }
                set
                {
                    _OutboundMailReplyToAddress = value;
                }
            }

            public string OutboundMailSenderAddress
            {
                get
                {
                    if ( string.IsNullOrEmpty( _OutboundMailSenderAddress ) )
                        _OutboundMailSenderAddress = DEFAULTFROM;
                    return _OutboundMailSenderAddress;
                }
                set
                {
                    _OutboundMailSenderAddress = value;
                }
            }

            public string OutboundMailServerAddress
            {
                get;
                set;
            }

            #endregion Properties
        }

        #endregion Nested Types
    }
}